#ifndef __SYMBOL_H
#define __SYMBOL_H
#include "compiler.h"

/* (#include "matrix.h") can't be included because of linker script may include this file */

/**
 * export the pointer symbol to ".#cluster" section
 * @cluster: the section contain in target elf, need describe in link script
 * @symbol: the symbol need to export, it can be function or variable
 * @level: the layout level in target elf, descending order
 * @example: SYMBOL_POINTER_EXPORT(init, system_init, 1) ;
 * @desc: you can execute the functions in cluster automatically, use following:
 *        symbol_execute_auto(init, funcytpe);
 * @note: describe in link script (just for above example(GCC)):
 *        .xxxx :                             .init :                     
 *        {                                   {
 *            . = ALIGN(8);                       . = ALIGN(8);
 *            xxxx_start = .;          ---->      init_start = .;
 *            KEEP(*(SORT(.xxxx_*)));             KEEP(*(SORT(.init_*)));
 *            xxxx_end = .;                       init_end = .;
 *        } > ????                            } > ????
 * @note: call `symbol_section_build` to quick build
 */
#if defined(MX_COMPILER_TYPE_AC6)
#define _SYMBOL_POINTER_EXPORT(cluster, symbol, level) \
            static mx_used mx_section(#cluster"_"#level) mx_void_pt cluster##_##symbol##_##level = symbol
#elif defined(MX_COMPILER_TYPE_GCC)
#define _SYMBOL_POINTER_EXPORT(cluster, symbol, level) \
            static mx_used mx_section("."#cluster"_"#level) mx_void_pt cluster##_##symbol##_##level = symbol
#endif
/* resolve macro not expanding problem */
#define SYMBOL_POINTER_EXPORT(cluster, symbol, level) _SYMBOL_POINTER_EXPORT(cluster, symbol, level)

/**
 * export the object symbol to ".#cluster" section
 * @note: this macro is same as 'SYMBOL_POINTER_EXPORT' but is for non-pointer variable
 */
#if defined(MX_COMPILER_TYPE_AC6)
#define _SYMBOL_OBJECT_EXPORT(cluster, symbol, level)   \
    static mx_used mx_section(#cluster"_"#level) mx_void_pt cluster##_##symbol##_##level = (mx_void_pt)&symbol
#elif defined(MX_COMPILER_TYPE_GCC)
#define _SYMBOL_OBJECT_EXPORT(cluster, symbol, level)   \
    static mx_used mx_section("."#cluster"_"#level) mx_void_pt cluster##_##symbol##_##level = (mx_void_pt)&symbol
#endif
/* resolve macro not expanding problem */
#define SYMBOL_OBJECT_EXPORT(cluster, symbol, level) _SYMBOL_OBJECT_EXPORT(cluster, symbol, level)

/**
 * execute functions automatically by specified #cluster
 * @prototype: void _symbol_execute_auto (cluster, functype);
 * @param cluster: the specified cluster section
 * @param functype: the type of the pointer (function)
 * @note: the symbol in the specified cluster section must be funciton type
 */
#if defined(MX_COMPILER_TYPE_AC6)
#define _symbol_execute_auto(cluster, functype) {   \
    extern mx_addr_t Image$$##cluster##$$Base;  \
    extern mx_addr_t Image$$##cluster##$$Limit; \
    for (mx_addr_pt next = (mx_addr_pt)&Image$$##cluster##$$Base;   \
         next < (mx_addr_pt)&Image$$##cluster##$$Limit; next++) {   \
        ((functype)*next)();    \
    }   \
}
#elif defined(MX_COMPILER_TYPE_GCC)
#define _symbol_execute_auto(cluster, functype) {  \
    extern mx_addr_t cluster##_start;   \
    extern mx_addr_t cluster##_end;     \
    for (mx_addr_pt next = (mx_addr_pt)&cluster##_start;    \
         next < (mx_addr_pt)&cluster##_end; next++) {  \
        ((functype)*next)();   \
    }   \
}
#endif
/* resolve macro not expanding problem */
#define symbol_execute_auto(cluster, functype) _symbol_execute_auto(cluster, functype)

/**
 * traverse each symbol based on the specified cluster
 * @prototype: void _symbol_foreach (cluster, var, { ... });
 * @param cluster: the specified cluster section
 * @param var: every symbol
 */
#if defined(MX_COMPILER_TYPE_AC6)
#define _symbol_foreach(cluster, var, ...) { \
    extern mx_addr_t Image$$##cluster##$$Base;   \
    extern mx_addr_t Image$$##cluster##$$Limit; \
    for (mx_addr_pt next = (mx_addr_pt)&Image$$##cluster##$$Base;    \
         next < (mx_addr_pt)&Image$$##cluster##$$Limit; next++) {   \
        mx_void_pt var = (mx_void_pt)*next; \
        __VA_ARGS__ \
    }   \
}
#elif defined(MX_COMPILER_TYPE_GCC)
#define _symbol_foreach(cluster, var, ...) { \
    extern mx_addr_t cluster##_start;   \
    extern mx_addr_t cluster##_end; \
    for (mx_addr_pt next = (mx_addr_pt)&cluster##_start;    \
         next < (mx_addr_pt)&cluster##_end; next++) {   \
        mx_void_pt var = (mx_void_pt)*next; \
        __VA_ARGS__ \
    }   \
}
#endif
/* resolve macro not expanding problem */
#define symbol_foreach(cluster, var, ...) _symbol_foreach(cluster, var, __VA_ARGS__);

/**
 * bild section in linker script (for meta programming)
 * @param sname: section name
 * @param mregion: memory region for placement
 * @note: call it in linker script
 */
#if defined(MX_COMPILER_TYPE_AC6)
#define symbol_section_build(sname, mregion) \
    sname mregion SORTTYPE Alignment { \
        *(sname##_*) \
    }
#elif defined(MX_COMPILER_TYPE_GCC)
#define symbol_section_build(sname, mregion) \
    .sname : \
    {   \
        . = ALIGN(8);   \
        sname##_start = .; \
        KEEP(*(SORT(.##sname##_*))); \
        sname##_end = .;   \
    } > mregion
#endif

#endif
